package com.dataexa.ky.service.mxgraph.validator;

import com.dataexa.ky.core.mxgraph.io.mxCodec;
import com.dataexa.ky.core.mxgraph.model.mxCell;
import com.dataexa.ky.core.mxgraph.model.mxGraphModel;
import com.dataexa.ky.core.mxgraph.model.mxIGraphModel;
import com.dataexa.ky.core.mxgraph.view.mxGraph;
import com.dataexa.ky.jefflow.flowcell.JefFlowCell;
import com.dataexa.ky.jefflow.flowcell.JefFlowRoot;
import com.dataexa.ky.jefflow.mxgraph.MxGraphParser;
import com.dataexa.ky.jefflow.thread.JefFlowAutoExcutor;
import com.dataexa.ky.jefflow.thread.JefFlowAutoThreadPool;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 图完整性校验器
 */
public class DrawioGraphValidator {

    public static void main(String[] args) throws Exception {
        String xmlString = """
                <mxGraphModel dx="1426" dy="743" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
                                 <root>
                                   <mxCell id="0" />
                                   <mxCell id="1" parent="0" />
                                   <mxCell id="2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="3" target="4">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="3" value="这是什么1" style="nodeStyle;image=/img/text.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="240" y="140" width="120" height="60" as="geometry" />
                                     <Object name="这是什么1" nodeName="text" icon="/img/text.png" fullPath="/node1" context="process" operation="step1" as="data" />
                                   </mxCell>
                                   <mxCell id="4" value="这是什么2" style="nodeStyle;image=/img/text.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="240" y="260" width="120" height="60" as="geometry" />
                                     <Object name="这是什么2" nodeName="text" icon="/img/text.png" fullPath="/node2" context="process" operation="step2" as="data" />
                                   </mxCell>
                                   <mxCell id="5" value="作妖" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="6" target="7">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="6" value="这是什么3" style="nodeStyle;image=/img/text.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="200" y="380" width="120" height="60" as="geometry" />
                                     <Object name="这是什么3" nodeName="text" icon="/img/text.png" fullPath="/node3" context="process" operation="step3" as="data" />
                                   </mxCell>
                                   <mxCell id="9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=0.5;exitY=0.5;exitDx=0;exitDy=15;exitPerimeter=0;" edge="1" parent="1" source="10" target="3">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="10" value="开始" style="nodeStyle;image=/img/start.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="260" y="80" width="80" height="30" as="geometry" />
                                     <Object name="开始" nodeName="start" icon="/img/start.png" fullPath="/start" context="start" operation="init" as="data" />
                                   </mxCell>
                                   <mxCell id="11" value="结束" style="nodeStyle;image=/img/end.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="270" y="500" width="80" height="30" as="geometry" />
                                     <Object name="结束" nodeName="end" icon="/img/end.png" fullPath="/end" context="end" operation="finish" as="data" />
                                   </mxCell>
                                   <mxCell id="12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.433;entryY=0.067;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="4" target="6">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="13" value="否" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="7" target="3">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="15" value="是" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;whiteSpace=wrap;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="7" target="11">
                                     <mxGeometry relative="1" as="geometry" />
                                   </mxCell>
                                   <mxCell id="7" value="是否？" style="nodeStyle;image=/img/decision.png;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                                     <mxGeometry x="440" y="370" width="80" height="80" as="geometry" />
                                     <Object name="是否？" nodeName="decision" icon="/img/decision.png" fullPath="/decision" context="decision" operation="check" as="data" />
                                   </mxCell>
                                 </root>
                               </mxGraphModel>
                """;

        xmlString = """
                <mxGraphModel dx="1426" dy="743" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
            <root>
              <mxCell id="0" />
              <mxCell id="1" parent="0" />
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-1" target="rbw8v4ttzfcFgNR-p5Pj-2">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-1" value="这是什么1" zuomeng="作业的梦" style="rounded=1;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                <mxGeometry x="240" y="140" width="120" height="60" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-2" value="这是什么2" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                <mxGeometry x="240" y="260" width="120" height="60" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-3" target="rbw8v4ttzfcFgNR-p5Pj-9">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-15" value="作妖" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="rbw8v4ttzfcFgNR-p5Pj-10">
                <mxGeometry x="-0.1833" relative="1" as="geometry">
                  <mxPoint as="offset" />
                </mxGeometry>
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-3" value="这是什么3" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                <mxGeometry x="200" y="380" width="120" height="60" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0.5;exitDx=0;exitDy=15;exitPerimeter=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-4" target="rbw8v4ttzfcFgNR-p5Pj-1">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-4" value="开始" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.start" vertex="1" parent="1">
                <mxGeometry x="260" y="80" width="80" height="30" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-5" value="结束" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.start" vertex="1" parent="1">
                <mxGeometry x="270" y="500" width="80" height="30" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.433;entryY=0.067;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-2" target="rbw8v4ttzfcFgNR-p5Pj-3">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-9" target="rbw8v4ttzfcFgNR-p5Pj-1">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-13" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="rbw8v4ttzfcFgNR-p5Pj-11">
                <mxGeometry x="-0.475" y="-2" relative="1" as="geometry">
                  <mxPoint as="offset" />
                </mxGeometry>
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="1" source="rbw8v4ttzfcFgNR-p5Pj-9" target="rbw8v4ttzfcFgNR-p5Pj-5">
                <mxGeometry relative="1" as="geometry" />
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-14" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="rbw8v4ttzfcFgNR-p5Pj-12">
                <mxGeometry x="-0.1455" y="-2" relative="1" as="geometry">
                  <mxPoint as="offset" />
                </mxGeometry>
              </mxCell>
              <mxCell id="rbw8v4ttzfcFgNR-p5Pj-9" value="是否？" style="rhombus;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                <mxGeometry x="440" y="370" width="80" height="80" as="geometry" />
              </mxCell>
            </root>
          </mxGraphModel>
                """;

        xmlString = """
                <mxGraphModel dx="1426" dy="743" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
                      <root>
                        <mxCell id="0" />
                        <mxCell id="1" parent="0" />
                        <mxCell id="2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" source="3" target="4" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                      <mxCell style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
                        <mxGeometry x="240" y="140" width="120" height="60" as="geometry" />
                      </mxCell>
                        <mxCell id="4" value="流程2" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                          <mxGeometry x="240" y="260" width="120" height="60" as="geometry" />
                        </mxCell>
                        <mxCell id="5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" source="7" target="16" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                        <mxCell id="6" value="执行" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="5">
                          <mxGeometry x="-0.1833" relative="1" as="geometry">
                            <mxPoint as="offset" />
                          </mxGeometry>
                        </mxCell>
                        <mxCell id="7" value="流程3" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#008a00;fontColor=#ffffff;strokeColor=#005700;arcSize=50;" vertex="1" parent="1">
                          <mxGeometry x="200" y="380" width="120" height="60" as="geometry" />
                        </mxCell>
                        <mxCell id="8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0.5;exitDx=0;exitDy=15;exitPerimeter=0;" edge="1" source="9" target="3" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                        <mxCell id="9" value="开始" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.start" vertex="1" parent="1">
                          <mxGeometry x="260" y="80" width="80" height="30" as="geometry" />
                          <Object name="开始" icon="/img/start.f26db4f5.png" nodeName="start" fullPath="" context="https://www.baidu.com/" operation="" as="data" />
                        </mxCell>
                        <mxCell id="10" value="结束" style="html=1;dashed=0;whiteSpace=wrap;shape=mxgraph.dfd.start" vertex="1" parent="1">
                          <mxGeometry x="270" y="500" width="80" height="30" as="geometry" />
                          <Object name="结束" icon="/img/over.61db7b65.png" nodeName="over" fullPath="" context="" operation="" as="data" />
                        </mxCell>
                        <mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.433;entryY=0.067;entryDx=0;entryDy=0;entryPerimeter=0;dashed=1;dashPattern=8 8;" edge="1" source="4" target="7" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                        <mxCell id="12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;dashed=1;" edge="1" source="16" target="3" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                        <mxCell id="13" value="否" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="12">
                          <mxGeometry x="-0.475" y="-2" relative="1" as="geometry">
                            <mxPoint as="offset" />
                          </mxGeometry>
                        </mxCell>
                        <mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" source="16" target="10" parent="1">
                          <mxGeometry relative="1" as="geometry" />
                        </mxCell>
                        <mxCell id="15" value="是" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="14">
                          <mxGeometry x="-0.1455" y="-2" relative="1" as="geometry">
                            <mxPoint as="offset" />
                          </mxGeometry>
                        </mxCell>
                        <mxCell id="16" value="是否？" style="rhombus;whiteSpace=wrap;html=1;" vertex="1" parent="1">
                          <mxGeometry x="440" y="370" width="80" height="80" as="geometry" />
                        </mxCell>
                      </root>
                    </mxGraphModel>
                """;

        xmlString = """
                <mxGraphModel>
                  <root>
                    <mxCell id="0" />
                    <mxCell id="1" parent="0" />
                    <mxCell id="2" value="初始化百度网页" style="nodeStyle;image=/img/start.f26db4f5.png" parent="1" vertex="1">
                      <mxGeometry x="220" y="80" width="130" height="35" as="geometry" />
                      <Object name="开始" icon="/img/start.f26db4f5.png" nodeName="start" fullPath="" context="https://www.baidu.com/" operation="" as="data" />
                    </mxCell>
                    <mxCell id="3" value="输入搜索内容" style="nodeStyle;image=/img/text.ab22920f.png" parent="1" vertex="1">
                      <mxGeometry x="220" y="170" width="130" height="35" as="geometry" />
                      <Object name="文本框" icon="/img/text.ab22920f.png" nodeName="text" fullPath="/html/body/div[1]/div[1]/div[5]/div/div/form/span[1]/input" context="hello world" operation="" as="data" />
                    </mxCell>
                    <mxCell id="4" value="点击百度一下" style="nodeStyle;image=/img/button.144b855d.png" parent="1" vertex="1">
                      <mxGeometry x="220" y="250" width="130" height="35" as="geometry" />
                      <Object name="按钮" icon="/img/button.144b855d.png" nodeName="button" fullPath="/html/body/div[1]/div[1]/div[5]/div/div/form/span[2]/input" context="" operation="3" as="data" />
                    </mxCell>
                    <mxCell id="5" parent="1" source="2" target="3" edge="1">
                      <mxGeometry relative="1" as="geometry" />
                    </mxCell>
                    <mxCell id="6" parent="1" source="3" target="4" edge="1">
                      <mxGeometry relative="1" as="geometry" />
                    </mxCell>
                    <mxCell id="7" value="结束" style="nodeStyle;image=/img/over.61db7b65.png" parent="1" vertex="1">
                      <mxGeometry x="220" y="490" width="130" height="35" as="geometry" />
                      <Object name="结束" icon="/img/over.61db7b65.png" nodeName="over" fullPath="" context="" operation="" as="data" />
                    </mxCell>
                    <mxCell id="8" value="延时器" style="nodeStyle;image=/img/delayed.288d27d6.png" vertex="1" parent="1">
                      <mxGeometry x="220" y="360" width="130" height="35" as="geometry" />
                      <Object name="延时器" icon="/img/delayed.288d27d6.png" nodeName="delayed" fullPath="" context="" operation="20000" as="data" />
                    </mxCell>
                    <mxCell id="9" edge="1" parent="1" source="4" target="8">
                      <mxGeometry relative="1" as="geometry" />
                    </mxCell>
                    <mxCell id="10" edge="1" parent="1" source="8" target="7">
                      <mxGeometry relative="1" as="geometry" />
                    </mxCell>
                  </root>
                </mxGraphModel>
                """;

        MxGraphParser graphParser = new MxGraphParser(xmlString);
        JefFlowRoot mxGraphModel = graphParser.parseMxGraph();
        JefFlowCell startCell = mxGraphModel.getStartCell();
        String flowId = "test";
        JefFlowAutoThreadPool.getThreadPool(flowId).execute(new Runnable() {
            @Override
            public void run() {
                new JefFlowAutoExcutor().run(startCell, flowId);
                //autoUiExcutor.run(startCell, flowId);
            }
        });

        if(true) {
            return;
        }

        // 解析 XML
        mxGraph graph = parseDrawIOXML(xmlString);
        if (graph == null) {
            System.out.println("解析失败，graph 为 null，退出。");
            return;
        }
        // 检查悬挂边
        checkDanglingEdges(graph);

        // 检查孤立节点
        checkIsolatedNodes(graph);

        // 检查连通性
        mxGraphModel model = (mxGraphModel) graph.getModel();
        Object startVertex = findStartVertex(model);
        if (startVertex != null) {
            checkGraphConnectivity(graph, startVertex);
        } else {
            System.out.println("未找到起点，无法检查连通性。");
        }
    }

    /**
     * 安全解析 draw.io XML 的方法
     */
    public static mxGraph parseDrawIOXML(String xmlString) {
        try {
            // 1. 解析XML文档
            Document document = parseXmlDocumentStrict(xmlString);

            // 2. 创建mxCodec实例
            mxCodec codec = new mxCodec(document);

            // 3. 查找mxGraphModel节点
            Node graphModelNode = findGraphModelNode(document);
            if (graphModelNode == null) {
                throw new IllegalArgumentException("XML中未找到mxGraphModel节点");
            }

            // 4. 关键修复：调整mxCell节点顺序（顶点在前，边在后）
            //reorderMxCellNodes(graphModelNode);

            // 5. 解码并设置模型
            Object decoded = codec.decode(graphModelNode);
            if (decoded instanceof mxIGraphModel) {
                mxGraph graph = new mxGraph();
                graph.setModel((mxIGraphModel) decoded);
                return graph;
            } else {
                // 增加调试信息：输出解码结果的实际类型
                //String decodedType = (decoded != null) ? decoded.getClass().getName() : "null";
                throw new IllegalArgumentException("解码结果不是有效的图形模型，实际类型: " +"xxx");
            }

        } catch (Exception e) {
            throw new IllegalArgumentException("解析 draw.io XML 失败: " + e.getMessage(), e);
        }
    }

    /**
     * 调整mxCell节点顺序：将所有vertex（顶点）移到edge（边）之前
     * 解决mxCodec解码时边引用的顶点尚未创建的问题
     */
    private static void reorderMxCellNodes(Node graphModelNode) {
        // 1. 找到root节点
        Node rootNode = null;
        for (int i = 0; i < graphModelNode.getChildNodes().getLength(); i++) {
            Node child = graphModelNode.getChildNodes().item(i);
            if ("root".equals(child.getNodeName())) {
                rootNode = child;
                break;
            }
        }
        if (rootNode == null) {
            throw new IllegalArgumentException("mxGraphModel节点下未找到root子节点");
        }

        // 2. 分离顶点和边节点
        List<Node> vertexNodes = new ArrayList<>();
        List<Node> edgeNodes = new ArrayList<>();
        Set<String> vertexIds = new HashSet<>(); // 跟踪所有顶点ID
        NodeList childNodes = rootNode.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            Node cellNode = childNodes.item(i);
            if (!"mxCell".equals(cellNode.getNodeName())) {
                continue; // 只处理mxCell节点
            }

            // 修复空ID
            String id = getAttributeValue(cellNode, "id");
            if (id == null || id.trim().isEmpty()) {
                id = "auto_gen_" + UUID.randomUUID().toString();
                Attr idAttr = cellNode.getOwnerDocument().createAttribute("id");
                idAttr.setValue(id);
                cellNode.getAttributes().setNamedItem(idAttr);
                System.out.println("修复空ID节点，自动生成ID: " + id);
            }

            // 修复空parent属性（根节点除外）
            if (!"0".equals(id) && (cellNode.getAttributes().getNamedItem("parent") == null ||
                    getAttributeValue(cellNode, "parent") == null ||
                    getAttributeValue(cellNode, "parent").trim().isEmpty())) {
                Attr parentAttr = cellNode.getOwnerDocument().createAttribute("parent");
                parentAttr.setValue("1");
                cellNode.getAttributes().setNamedItem(parentAttr);
                System.out.println("修复空parent节点，ID: " + id + "，自动设置parent=1");
            }

            // 判断节点类型
            boolean isEdge = "1".equals(getAttributeValue(cellNode, "edge"));
            boolean isVertex = "1".equals(getAttributeValue(cellNode, "vertex"));

            // 修复style属性
            String style = getAttributeValue(cellNode, "style");
            StringBuilder newStyle = new StringBuilder();
            if (style == null || style.trim().isEmpty()) {
                if (isEdge) {
                    // 为边提供完整的默认样式
                    newStyle.append("edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;");
                } else if (isVertex) {
                    // 为顶点提供默认样式
                    newStyle.append("rounded=0;whiteSpace=wrap;html=1;");
                } else {
                    // 基础节点默认空样式
                    newStyle.append("");
                }
            } else {
                newStyle.append(style);
                // 为边添加必要的连接点属性
                if (isEdge) {
                    if (!style.contains("exitX=")) newStyle.append(";exitX=0.5");
                    if (!style.contains("exitY=")) newStyle.append(";exitY=1");
                    if (!style.contains("exitDx=")) newStyle.append(";exitDx=0");
                    if (!style.contains("exitDy=")) newStyle.append(";exitDy=0");
                    if (!style.contains("entryX=")) newStyle.append(";entryX=0.5");
                    if (!style.contains("entryY=")) newStyle.append(";entryY=0");
                    if (!style.contains("entryDx=")) newStyle.append(";entryDx=0");
                    if (!style.contains("entryDy=")) newStyle.append(";entryDy=0");
                    // 移除可能导致问题的exitPerimeter
                    if (style.contains("exitPerimeter=0")) {
                        newStyle = new StringBuilder(newStyle.toString().replace("exitPerimeter=0", ""));
                        System.out.println("移除边ID=" + id + "的exitPerimeter=0属性");
                    }
                }
            }
            Attr styleAttr = cellNode.getOwnerDocument().createAttribute("style");
            styleAttr.setValue(newStyle.toString().replaceAll(";;+", ";").replaceAll("^;|;$", ""));
            cellNode.getAttributes().setNamedItem(styleAttr);
            System.out.println("修复style属性，ID: " + id + ", 新样式: " + newStyle);

            // 修复ID中的特殊字符
            fixCellId(cellNode, isEdge);

            // 分类节点并收集顶点ID
            if (isVertex) {
                fixEmptyVertexValue(cellNode);
                vertexNodes.add(cellNode);
                vertexIds.add(id);
            } else if (isEdge) {
                // 验证边的source和target
                String source = getAttributeValue(cellNode, "source");
                String target = getAttributeValue(cellNode, "target");
                if (source == null || target == null || source.trim().isEmpty() || target.trim().isEmpty()) {
                    System.out.println("警告: 边节点ID=" + id + "缺少source或target属性，跳过添加");
                    continue;
                }
                // 验证source和target是否存在于顶点集合
                if (!vertexIds.contains(source) || !vertexIds.contains(target)) {
                    System.out.println("警告: 边节点ID=" + id + "的source=" + source + "或target=" + target + "引用了不存在的顶点，跳过添加");
                    continue;
                }
                edgeNodes.add(cellNode);
            } else {
                vertexNodes.add(cellNode);
                vertexIds.add(id);
            }
        }

        // 3. 重新排序：先顶点后边
        while (rootNode.hasChildNodes()) {
            rootNode.removeChild(rootNode.getFirstChild());
        }
        for (Node node : vertexNodes) {
            rootNode.appendChild(node);
        }
        for (Node node : edgeNodes) {
            rootNode.appendChild(node);
        }

        System.out.println("mxCell节点已重新排序 - 顶点数: " + vertexNodes.size() + ", 边数: " + edgeNodes.size());
    }

    /**
     * 修复单元格ID中的特殊字符
     */
    private static void fixCellId(Node cellNode, boolean isEdge) {
        String id = getAttributeValue(cellNode, "id");
        if (id != null && id.contains("-")) {
            String newId = id.replace("-", "_");
            cellNode.getAttributes().getNamedItem("id").setNodeValue(newId);

            // 如果是边，同时修复source和target属性
            if (isEdge) {
                fixAttributeValue(cellNode, "source", "-", "_");
                fixAttributeValue(cellNode, "target", "-", "_");
            }
            System.out.println("修复ID中的特殊字符: " + id + " -> " + newId);
        }
    }

    /**
     * 修复空值顶点的value属性
     */
    private static void fixEmptyVertexValue(Node cellNode) {
        String value = getAttributeValue(cellNode, "value");
        if (value == null || value.trim().isEmpty()) {
            cellNode.getAttributes().getNamedItem("value").setNodeValue("[空节点]");
            System.out.println("修复空值顶点: ID=" + getAttributeValue(cellNode, "id"));
        }
    }

    /**
     * 修复属性值中的特殊字符
     */
    private static void fixAttributeValue(Node node, String attrName, String oldChar, String newChar) {
        Node attr = node.getAttributes().getNamedItem(attrName);
        if (attr != null && attr.getNodeValue() != null && attr.getNodeValue().contains(oldChar)) {
            attr.setNodeValue(attr.getNodeValue().replace(oldChar, newChar));
        }
    }

    /**
     * 获取节点属性值（处理null情况）
     */
    private static String getAttributeValue(Node node, String attrName) {
        Node attr = node.getAttributes().getNamedItem(attrName);
        return (attr != null) ? attr.getNodeValue() : null;
    }

    // 详细的XML解析方法，带有完整调试信息
    public static Document parseXmlDocumentStrict(String xmlString) throws Exception {
        // 1. 验证输入
        if (xmlString == null) {
            throw new IllegalArgumentException("XML字符串不能为null");
        }
        if (xmlString.trim().isEmpty()) {
            throw new IllegalArgumentException("XML字符串不能为空");
        }

        // 2. 打印输入XML的基本信息
        System.out.println("=== 输入XML信息 ===");
        System.out.println("长度: " + xmlString.length() + " 字符");
        System.out.println("首100字符: " + xmlString.substring(0, Math.min(100, xmlString.length())));
        System.out.println("编码检测: " + detectEncoding(xmlString));
        System.out.println("===================");

        try {
            // 3. 配置文档构建工厂
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            System.out.println("使用的XML解析器: " + factory.getClass().getName());

            // 关键配置
            factory.setNamespaceAware(true);
            factory.setValidating(false);
            factory.setIgnoringComments(false);
            factory.setIgnoringElementContentWhitespace(false);

            // 禁用外部实体以提高安全性和避免解析失败
            try {
                factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
                factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
                factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            } catch (ParserConfigurationException e) {
                System.err.println("警告: 解析器不支持某些安全特性 - " + e.getMessage());
            }

            // 4. 创建文档构建器
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 设置详细错误处理器
            builder.setErrorHandler(new org.xml.sax.ErrorHandler() {
                @Override
                public void warning(SAXParseException e) throws SAXException {
                    System.err.println("[警告] 行 " + e.getLineNumber() + ", 列 " + e.getColumnNumber() + ": " + e.getMessage());
                }

                @Override
                public void error(SAXParseException e) throws SAXException {
                    System.err.println("[错误] 行 " + e.getLineNumber() + ", 列 " + e.getColumnNumber() + ": " + e.getMessage());
                    throw e; // 错误时中断解析
                }

                @Override
                public void fatalError(SAXParseException e) throws SAXException {
                    System.err.println("[致命错误] 行 " + e.getLineNumber() + ", 列 " + e.getColumnNumber() + ": " + e.getMessage());
                    throw e; // 致命错误时中断解析
                }
            });

            // 5. 准备输入源
            ByteArrayInputStream inputStream = new ByteArrayInputStream(
                    xmlString.getBytes(StandardCharsets.UTF_8)
            );
            InputSource inputSource = new InputSource(inputStream);
            inputSource.setEncoding("UTF-8"); // 明确指定编码

            // 6. 执行解析
            System.out.println("开始解析XML...");
            long startTime = System.currentTimeMillis();
            Document doc = builder.parse(inputSource);
            long endTime = System.currentTimeMillis();
            System.out.println("解析完成，耗时: " + (endTime - startTime) + "ms");

            // 7. 验证解析结果
            if (doc == null) {
                throw new RuntimeException("解析器返回null，但未抛出异常！");
            }

            // 8. 输出解析结果信息
            System.out.println("解析成功，文档信息:");
            System.out.println("文档实现类: " + doc.getClass().getName());
            System.out.println("根节点: " + doc.getDocumentElement().getNodeName());
            System.out.println("根节点属性数量: " + doc.getDocumentElement().getAttributes().getLength());
            System.out.println("子节点数量: " + doc.getDocumentElement().getChildNodes().getLength());

            // 9. 尝试转换为字符串验证
            String docXml = convertDocumentToString(doc);
            System.out.println("解析后XML长度: " + docXml.length() + " 字符");

            return doc;
        } catch (ParserConfigurationException e) {
            System.err.println("解析器配置失败: " + e.getMessage());
            throw new Exception("XML解析器配置错误", e);
        } catch (SAXException e) {
            System.err.println("XML语法解析失败: " + e.getMessage());
            throw new Exception("XML格式无效", e);
        } catch (Exception e) {
            System.err.println("解析过程发生错误: " + e.getMessage());
            throw new Exception("XML解析失败", e);
        }
    }

    // 检测XML声明中的编码
    private static String detectEncoding(String xml) {
        int start = xml.indexOf("<?xml");
        if (start == -1) return "未找到XML声明";

        int end = xml.indexOf("?>", start);
        if (end == -1) return "XML声明不完整";

        String decl = xml.substring(start, end);
        if (decl.contains("encoding=\"UTF-8\"") || decl.contains("encoding='UTF-8'")) {
            return "UTF-8 (声明中)";
        }

        return "声明中未指定或不是UTF-8";
    }

    // 将Document转换为字符串，用于验证解析结果
    private static String convertDocumentToString(Document doc) throws Exception {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));
        return writer.getBuffer().toString();
    }

    private static Document parseXmlDocument(String xmlString) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
        factory.setValidating(false);

        // 安全配置
        factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

        DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(new InputSource(new StringReader(xmlString)));
    }

    private static Node findGraphModelNode(Document document) {
        // 查找mxGraphModel节点
        return document.getElementsByTagName("mxGraphModel").item(0);
    }

    /**
     * 检查悬挂边
     * 悬挂边是指边的源（source）或目标（target）为空（即缺少连接的节点）。在 mxGraph Java 中，可以通过遍历所有边并检查 source 和 target 是否存在来实现。
     *
     * @param graph
     */
    public static void checkDanglingEdges(mxGraph graph) {
        mxGraphModel model = (mxGraphModel) graph.getModel();
        Object parent = model.getChildAt(model.getRoot(), 0);
        if (parent == null) {
            System.out.println("错误：图层为空，无法检查悬挂边。");
            return;
        }
        int childCount = model.getChildCount(parent);
        boolean hasDanglingEdges = false;
        for (int i = 0; i < childCount; i++) {
            Object cell = model.getChildAt(parent, i);
            if (model.isEdge(cell)) {
                mxCell edge = (mxCell) cell;
                if (edge.getSource() == null || edge.getTarget() == null) {
                    hasDanglingEdges = true;
                    System.out.println("发现悬挂边: ID=" + edge.getId() + ", 值=" + (edge.getValue() != null ? edge.getValue() : "无值"));
                }
            }
        }
        if (!hasDanglingEdges) {
            System.out.println("无悬挂边，所有边均完整连接。");
        }
    }

    /**
     * 检查孤立节点（野节点）
     * 孤立节点是没有入边或出边的节点（度为0）。可以通过检查每个节点的连接边来判断。
     *
     * @param graph
     */
    public static void checkIsolatedNodes(mxGraph graph) {
        mxGraphModel model = (mxGraphModel) graph.getModel();
        Object parent = model.getChildAt(model.getRoot(), 0);
        if (parent == null) {
            System.out.println("错误：图层为空，无法检查孤立节点。");
            return;
        }
        int childCount = model.getChildCount(parent);
        boolean hasIsolatedNodes = false;
        for (int i = 0; i < childCount; i++) {
            Object cell = model.getChildAt(parent, i);
            if (model.isVertex(cell)) {
                mxCell vertex = (mxCell) cell;
                System.out.println("节点：" + vertex.getId() + "；值：" + vertex.getValue() + "；属性:" + vertex.getAttribute("zuomeng"));
                int edgeCount = model.getEdgeCount(vertex);
                if (edgeCount == 0) {
                    hasIsolatedNodes = true;
                    System.out.println("发现孤立节点: ID=" + vertex.getId() + ", 值=" + (vertex.getValue() != null ? vertex.getValue() : "无值"));
                }
            }
        }
        if (!hasIsolatedNodes) {
            System.out.println("无孤立节点，所有节点均有连接。");
        }
    }

    /**
     * 检查流程图的连通性
     * 要验证流程图是否“完成连线”，可以检查图是否连通（即从某个起点能否到达所有节点）。这可以通过 BFS 或 DFS 实现。以下是 BFS 示例，假设你知道流程图的起点（或默认用第一个节点）。
     *
     * @param graph
     * @param startVertex
     */
    public static void checkGraphConnectivity(mxGraph graph, Object startVertex) {
        mxGraphModel model = (mxGraphModel) graph.getModel();
        Object parent = model.getChildAt(model.getRoot(), 0);
        if (parent == null || startVertex == null) {
            System.out.println("错误：图层或起点为空，无法检查连通性。");
            return;
        }

        Set<Object> allVertices = new HashSet<>();
        int childCount = model.getChildCount(parent);
        for (int i = 0; i < childCount; i++) {
            Object cell = model.getChildAt(parent, i);
            if (model.isVertex(cell)) {
                allVertices.add(cell);
            }
        }

        Set<Object> visited = new HashSet<>();
        Queue<Object> queue = new LinkedList<>();
        queue.add(startVertex);
        visited.add(startVertex);

        while (!queue.isEmpty()) {
            Object current = queue.poll();
            Object[] edges = graph.getEdges(current, parent, true, true, false, true);
            for (Object edge : edges) {
                mxCell mxEdge = (mxCell) edge;
                Object source = mxEdge.getSource();
                Object target = mxEdge.getTarget();
                if (source != null && source != current && !visited.contains(source)) {
                    visited.add(source);
                    queue.add(source);
                }
                if (target != null && target != current && !visited.contains(target)) {
                    visited.add(target);
                    queue.add(target);
                }
            }
        }

        if (visited.size() < allVertices.size()) {
            System.out.println("图不连通，可能存在未完成连线或孤立节点。");
            allVertices.removeAll(visited);
            for (Object vertex : allVertices) {
                mxCell mxVertex = (mxCell) vertex;
                System.out.println("未连接节点: ID=" + mxVertex.getId() + ", 值=" + (mxVertex.getValue() != null ? mxVertex.getValue() : "无值"));
            }
        } else {
            System.out.println("图连通，所有节点可从起点到达。");
        }
    }

    /**
     * 选择起点
     * 如果流程图有明确的起点（例如标有“Start”的节点），可以通过 mxCell.getValue() 查找。
     *
     * @param model
     * @return
     */
    private static Object findStartVertex(mxGraphModel model) {
        Object parent = model.getChildAt(model.getRoot(), 0);
        if (parent == null) return null;
        int childCount = model.getChildCount(parent);
        for (int i = 0; i < childCount; i++) {
            Object cell = model.getChildAt(parent, i);
            if (model.isVertex(cell)) {
                mxCell vertex = (mxCell) cell;
                String value = vertex.getValue() != null ? vertex.getValue().toString() : "";
                String style = vertex.getStyle() != null ? vertex.getStyle() : "";
                if (value.equalsIgnoreCase("Start") || style.contains("ellipse") || style.contains("start")) {
                    return vertex;
                }
            }
        }
        for (int i = 0; i < childCount; i++) {
            Object cell = model.getChildAt(parent, i);
            if (model.isVertex(cell)) {
                return cell;
            }
        }
        return null;
    }
}